<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>幽灵古宅 - 3D场景展示</title>
    <!-- 配置Three.js模块导入映射 -->
    <script type="importmap">
    {
        "imports": {
            "three": "https://threelab.cn/openthree/threejs/build/three.module.js",
            "three/addons/": "https://threelab.cn/openthree/threejs/examples/jsm/"
        }
    }
    </script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            background-color: #1a1a2e;
            width: 100vw;
            height: 100vh;
            overflow: hidden;
        }
        
        #sceneContainer {
            width: 100%;
            height: 100%;
        }
        
        .scene-info {
            position: fixed;
            bottom: 20px;
            left: 20px;
            background-color: rgba(10, 10, 20, 0.7);
            color: #e0e0f0;
            padding: 10px 15px;
            border-radius: 6px;
            font-family: 'Segoe UI', sans-serif;
            font-size: 13px;
            backdrop-filter: blur(4px);
            z-index: 10;
        }
    </style>
</head>
<body>
    <!-- 3D场景容器 -->
    <div id="sceneContainer"></div>
    
    <!-- 场景信息面板 -->
    <div class="scene-info">
        操作提示: 鼠标拖动旋转视角 | 滚轮缩放 | 右键拖动平移 | 观察幽灵的移动轨迹
    </div>

    <script type="module">
        // 导入Three.js核心模块和所需组件
        import * as THREE from "three";
        import { OrbitControls } from "three/addons/controls/OrbitControls.js";
        import { GUI } from "three/addons/libs/lil-gui.module.min.js";

        /**
         * 工具函数：生成指定范围内的随机整数（包含首尾）
         * @param {number} min - 最小值
         * @param {number} max - 最大值
         * @returns {number} 随机整数
         */
        const getRandomIntInRange = (min, max) => {
            const minVal = Math.ceil(min);
            const maxVal = Math.floor(max);
            return Math.floor(Math.random() * (maxVal - minVal + 1) + minVal);
        };

        /**
         * 工具函数：为网格添加uv2属性以支持AO贴图
         * @param {THREE.Mesh} mesh - 需要处理的网格对象
         */
        const setupAoMap = (mesh) => {
            // 复制uv属性到uv2，用于环境光遮蔽贴图
            mesh.geometry.setAttribute(
                'uv2',
                new THREE.Float32BufferAttribute(mesh.geometry.attributes.uv.array, 2)
            );
        };

        /**
         * 主场景类：封装整个3D场景的创建和管理
         */
        class HauntedHouseScene {
            constructor(containerId) {
                // 获取场景容器
                this.container = document.getElementById(containerId);
                
                // 初始化核心组件
                this.initScene();
                this.initCamera();
                this.initRenderer();
                this.initControls();
                
                // 加载纹理资源
                this.loadTextures();
                
                // 场景对象引用
                this.objects = {};
                this.lights = {};
                
                // 动画时钟
                this.clock = new THREE.Clock();
                
                // 绑定窗口大小变化事件
                window.addEventListener('resize', () => this.handleResize());
            }
            
            /**
             * 初始化场景
             */
            initScene() {
                this.scene = new THREE.Scene();
                
                // 添加雾效增强氛围
                this.fog = new THREE.Fog('#2a2a4a', 1, 18);
                this.scene.fog = this.fog;
            }
            
            /**
             * 初始化相机
             */
            initCamera() {
                this.camera = new THREE.PerspectiveCamera(
                    70,  // 视野角度
                    this.container.clientWidth / this.container.clientHeight,  // 宽高比
                    0.1,  // 近平面
                    100   // 远平面
                );
                
                // 设置相机初始位置
                this.camera.position.set(5, 3, 7);
                this.camera.lookAt(0, 1, 0);
            }
            
            /**
             * 初始化渲染器
             */
            initRenderer() {
                this.renderer = new THREE.WebGLRenderer({
                    antialias: true,  // 启用抗锯齿
                    powerPreference: 'high-performance'  // 优先考虑性能
                });
                
                // 设置渲染器尺寸和背景色
                this.renderer.setSize(
                    this.container.clientWidth,
                    this.container.clientHeight
                );
                this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
                this.renderer.setClearColor('#2a2a4a');
                
                // 启用阴影并设置阴影类型
                this.renderer.shadowMap.enabled = true;
                this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
                
                // 将渲染器添加到容器
                this.container.appendChild(this.renderer.domElement);
            }
            
            /**
             * 初始化控制器
             */
            initControls() {
                this.controls = new OrbitControls(
                    this.camera, 
                    this.renderer.domElement
                );
                
                // 配置控制器属性
                this.controls.enableDamping = true;
                this.controls.dampingFactor = 0.1;
                this.controls.rotateSpeed = 0.4;
                this.controls.zoomSpeed = 0.6;
                this.controls.enablePan = true;
                this.controls.panSpeed = 0.5;
            }
            
            /**
             * 加载场景所需的纹理资源
             */
            loadTextures() {
                // 创建加载管理器监控加载状态
                this.loadingManager = new THREE.LoadingManager();
                this.loadingManager.onLoad = () => {
                    console.log('所有纹理加载完成');
                    // 纹理加载完成后创建场景内容
                    this.createSceneContent();
                    this.setupGUI();
                    this.startAnimation();
                };
                
                this.loadingManager.onError = (url) => {
                    console.error(`纹理加载失败: ${url}`);
                };
                
                // 创建纹理加载器
                this.textureLoader = new THREE.TextureLoader(this.loadingManager);
                
                // 加载门的纹理
                this.textures = {
                    door: {
                        color: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/color.jpg',
                            (tex) => tex.colorSpace = THREE.SRGBColorSpace
                        ),
                        alpha: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/alpha.jpg'
                        ),
                        ao: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/ambientOcclusion.jpg'
                        ),
                        height: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/height.jpg'
                        ),
                        metalness: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/metalness.jpg'
                        ),
                        normal: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/normal.jpg'
                        ),
                        roughness: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/door/roughness.jpg'
                        )
                    },
                    
                    // 加载砖块纹理
                    bricks: {
                        color: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/bricks/color.jpg',
                            (tex) => tex.colorSpace = THREE.SRGBColorSpace
                        ),
                        ao: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/bricks/ambientOcclusion.jpg'
                        ),
                        normal: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/bricks/normal.jpg'
                        ),
                        roughness: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/bricks/roughness.jpg'
                        )
                    },
                    
                    // 加载草地纹理
                    grass: {
                        color: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/grass/color.jpg',
                            (tex) => tex.colorSpace = THREE.SRGBColorSpace
                        ),
                        ao: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/grass/ambientOcclusion.jpg'
                        ),
                        normal: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/grass/normal.jpg'
                        ),
                        roughness: this.textureLoader.load(
                            'https://AivoGenX.github.io/web3d-file-server/threeExamples/application/haunted_house/grass/roughness.jpg'
                        )
                    }
                };
            }
            
            /**
             * 创建场景中的所有物体
             */
            createSceneContent() {
                // 设置草地纹理的重复属性
                this.setupGrassTextures();
                
                // 创建地面
                this.createGround();
                
                // 创建房屋组
                this.createHouse();
                
                // 创建墓碑群
                this.createGravestones();
                
                // 创建光源
                this.createLights();
                
                // 创建幽灵
                this.createGhosts();
            }
            
            /**
             * 设置草地纹理的重复和包裹模式
             */
            setupGrassTextures() {
                const grassTextures = this.textures.grass;
                const repeat = new THREE.Vector2(8, 8);
                
                grassTextures.color.wrapS = THREE.RepeatWrapping;
                grassTextures.color.wrapT = THREE.RepeatWrapping;
                grassTextures.color.repeat.copy(repeat);
                
                grassTextures.ao.wrapS = THREE.RepeatWrapping;
                grassTextures.ao.wrapT = THREE.RepeatWrapping;
                grassTextures.ao.repeat.copy(repeat);
                
                grassTextures.normal.wrapS = THREE.RepeatWrapping;
                grassTextures.normal.wrapT = THREE.RepeatWrapping;
                grassTextures.normal.repeat.copy(repeat);
                
                grassTextures.roughness.wrapS = THREE.RepeatWrapping;
                grassTextures.roughness.wrapT = THREE.RepeatWrapping;
                grassTextures.roughness.repeat.copy(repeat);
            }
            
            /**
             * 创建地面
             */
            createGround() {
                const groundGeometry = new THREE.PlaneGeometry(40, 40);
                const groundMaterial = new THREE.MeshStandardMaterial({
                    map: this.textures.grass.color,
                    aoMap: this.textures.grass.ao,
                    aoMapIntensity: 1.5,
                    normalMap: this.textures.grass.normal,
                    roughnessMap: this.textures.grass.roughness
                });
                
                this.objects.ground = new THREE.Mesh(groundGeometry, groundMaterial);
                setupAoMap(this.objects.ground);
                
                // 旋转地面使其水平
                this.objects.ground.rotation.x = -Math.PI / 2;
                this.objects.ground.position.y = 0;
                
                // 配置阴影属性
                this.objects.ground.receiveShadow = true;
                this.objects.ground.castShadow = false;
                
                this.scene.add(this.objects.ground);
            }
            
            /**
             * 创建房屋及其组件
             */
            createHouse() {
                // 房屋尺寸配置
                const houseConfig = {
                    width: 4,
                    height: 2.5,
                    depth: 4,
                    roofHeight: 1.2,
                    doorSize: 2.2
                };
                
                // 创建房屋组
                this.objects.house = new THREE.Group();
                this.scene.add(this.objects.house);
                
                // 创建墙壁
                this.createWalls(houseConfig);
                
                // 创建屋顶
                this.createRoof(houseConfig);
                
                // 创建门
                this.createDoor(houseConfig);
                
                // 创建灌木丛
                this.createBushes();
            }
            
            /**
             * 创建房屋墙壁
             * @param {Object} config - 房屋尺寸配置
             */
            createWalls(config) {
                const wallGeometry = new THREE.BoxGeometry(
                    config.width, 
                    config.height, 
                    config.depth
                );
                
                const wallMaterial = new THREE.MeshStandardMaterial({
                    map: this.textures.bricks.color,
                    aoMap: this.textures.bricks.ao,
                    aoMapIntensity: 2,
                    normalMap: this.textures.bricks.normal,
                    roughnessMap: this.textures.bricks.roughness
                });
                
                this.objects.walls = new THREE.Mesh(wallGeometry, wallMaterial);
                setupAoMap(this.objects.walls);
                
                // 设置墙壁位置
                this.objects.walls.position.y = config.height * 0.5;
                
                // 配置阴影属性
                this.objects.walls.castShadow = true;
                this.objects.walls.receiveShadow = true;
                
                this.objects.house.add(this.objects.walls);
            }
            
            /**
             * 创建屋顶
             * @param {Object} config - 房屋尺寸配置
             */
            createRoof(config) {
                // 计算屋顶尺寸
                const roofRadius = Math.sqrt(
                    (config.width * 0.5) **2 + (config.depth * 0.5)** 2
                );
                
                const roofGeometry = new THREE.ConeGeometry(
                    roofRadius * 1.05,  // 半径
                    config.roofHeight,  // 高度
                    4                   // 边数（4边形）
                );
                
                const roofMaterial = new THREE.MeshStandardMaterial({
                    color: '#8b4513'
                });
                
                this.objects.roof = new THREE.Mesh(roofGeometry, roofMaterial);
                
                // 设置屋顶位置和旋转
                this.objects.roof.rotation.y = Math.PI / 4;
                this.objects.roof.position.y = config.height + config.roofHeight * 0.5;
                
                // 配置阴影属性
                this.objects.roof.castShadow = true;
                this.objects.roof.receiveShadow = true;
                
                this.objects.house.add(this.objects.roof);
            }
            
            /**
             * 创建门
             * @param {Object} config - 房屋尺寸配置
             */
            createDoor(config) {
                const doorGeometry = new THREE.PlaneGeometry(
                    config.doorSize, 
                    config.doorSize, 
                    100, 100  // 增加分段数以获得更好的位移贴图效果
                );
                
                const doorMaterial = new THREE.MeshStandardMaterial({
                    map: this.textures.door.color,
                    transparent: true,
                    alphaMap: this.textures.door.alpha,
                    aoMap: this.textures.door.ao,
                    aoMapIntensity: 4,
                    displacementMap: this.textures.door.height,
                    displacementScale: 0.1,
                    normalMap: this.textures.door.normal,
                    metalnessMap: this.textures.door.metalness,
                    roughnessMap: this.textures.door.roughness
                });
                
                this.objects.door = new THREE.Mesh(doorGeometry, doorMaterial);
                setupAoMap(this.objects.door);
                
                // 设置门的位置
                this.objects.door.position.y = config.doorSize * 0.5;
                this.objects.door.position.z = config.depth * 0.5 + 0.01;  // 稍微向外以避免与墙壁重叠
                
                // 配置阴影属性
                this.objects.door.castShadow = true;
                this.objects.door.receiveShadow = true;
                
                this.objects.house.add(this.objects.door);
            }
            
            /**
             * 创建房屋周围的灌木丛
             */
            createBushes() {
                const bushGeometry = new THREE.SphereGeometry(1, 16, 16);
                const bushMaterial = new THREE.MeshStandardMaterial({
                    color: '#4a7a2c'
                });
                
                // 创建多个灌木丛并设置不同位置和大小
                const bushPositions = [
                    { x: 2.1, z: 2.1, scale: 0.5 },  // 右前方
                    { x: -2.1, z: -2.1, scale: 0.8 }, // 左后方
                    { x: 2.1, z: -2.1, scale: 0.3 },  // 右后方
                    { x: -2.1, z: 2.1, scale: 0.6 }   // 左前方
                ];
                
                this.objects.bushes = [];
                bushPositions.forEach(pos => {
                    const bush = new THREE.Mesh(bushGeometry, bushMaterial);
                    bush.position.set(pos.x, pos.scale * 0.5, pos.z);
                    bush.scale.set(pos.scale, pos.scale, pos.scale);
                    
                    // 配置阴影属性
                    bush.castShadow = true;
                    bush.receiveShadow = true;
                    
                    this.objects.house.add(bush);
                    this.objects.bushes.push(bush);
                });
            }
            
            /**
             * 创建墓碑群
             */
            createGravestones() {
                this.objects.graves = new THREE.Group();
                this.scene.add(this.objects.graves);
                
                const graveGeometry = new THREE.BoxGeometry(0.4, 0.8, 0.2);
                const graveMaterial = new THREE.MeshStandardMaterial({
                    color: '#b0b0b0'
                });
                
                // 创建多个随机分布的墓碑
                const graveCount = 50;
                const minRadius = 5;
                const maxRadius = 12;
                
                for (let i = 0; i < graveCount; i++) {
                    const grave = new THREE.Mesh(graveGeometry, graveMaterial);
                    
                    // 随机位置（环形分布）
                    const angle = Math.random() * Math.PI * 2;
                    const radius = minRadius + Math.random() * (maxRadius - minRadius);
                    
                    grave.position.x = Math.cos(angle) * radius;
                    grave.position.z = Math.sin(angle) * radius;
                    grave.position.y = 0.4 + Math.random() * 0.3;  // 稍微随机高度
                    
                    // 随机旋转
                    grave.rotation.y = Math.random() * Math.PI;
                    grave.rotation.z = (Math.random() - 0.5) * 0.3;
                    
                    // 配置阴影属性
                    grave.castShadow = true;
                    grave.receiveShadow = true;
                    
                    this.objects.graves.add(grave);
                }
            }
            
            /**
             * 创建场景中的光源
             */
            createLights() {
                // 环境光
                this.lights.ambient = new THREE.AmbientLight('#b9d5ff', 0.12);
                this.scene.add(this.lights.ambient);
                
                // 月光（方向光）
                this.lights.moon = new THREE.DirectionalLight('#e6eeff', 0.12);
                this.lights.moon.position.set(4, 5, -2);
                this.scene.add(this.lights.moon);
                
                // 门灯（点光源）
                this.lights.door = new THREE.PointLight('#ff9d5c', 1.2, 7);
                this.lights.door.position.set(0, 2.2, 2.7);
                this.objects.house.add(this.lights.door);
                
                // 配置光源阴影
                this.configureLightShadows();
            }
            
            /**
             * 配置光源阴影属性
             */
            configureLightShadows() {
                // 启用光源阴影
                this.lights.moon.castShadow = true;
                this.lights.door.castShadow = true;
                
                // 配置月光阴影
                this.lights.moon.shadow.mapSize.set(2048, 2048);
                this.lights.moon.shadow.camera.near = 1;
                this.lights.moon.shadow.camera.far = 20;
                
                // 配置门灯阴影
                this.lights.door.shadow.mapSize.set(512, 512);
                this.lights.door.shadow.camera.near = 0.1;
                this.lights.door.shadow.camera.far = 10;
            }
            
            /**
             * 创建幽灵（移动的点光源）
             */
            createGhosts() {
                // 幽灵1 - 粉红色
                this.objects.ghost1 = new THREE.PointLight('#ff00ff', 2, 5);
                this.scene.add(this.objects.ghost1);
                
                // 幽灵2 - 绿色
                this.objects.ghost2 = new THREE.PointLight('#00ff88', 2, 5);
                this.scene.add(this.objects.ghost2);
                
                // 幽灵3 - 蓝色
                this.objects.ghost3 = new THREE.PointLight('#00aaff', 2, 5);
                this.scene.add(this.objects.ghost3);
                
                // 配置幽灵阴影
                [this.objects.ghost1, this.objects.ghost2, this.objects.ghost3].forEach(ghost => {
                    ghost.castShadow = true;
                    ghost.shadow.mapSize.set(256, 256);
                    ghost.shadow.camera.far = 8;
                });
            }
            
            /**
             * 设置GUI控制面板
             */
            setupGUI() {
                this.gui = new GUI({
                    title: '场景控制',
                    width: 300,
                    closeFolders: true
                });
                
                // 光源控制组
                const lightFolder = this.gui.addFolder('光源设置');
                lightFolder.add(this.lights.ambient, 'intensity', 0, 0.5, 0.01).name('环境光强度');
                lightFolder.add(this.lights.moon, 'intensity', 0, 0.5, 0.01).name('月光强度');
                lightFolder.add(this.lights.door, 'intensity', 0, 3, 0.01).name('门灯强度');
                
                // 月光位置控制
                const moonPosFolder = lightFolder.addFolder('月光位置');
                moonPosFolder.add(this.lights.moon.position, 'x', -10, 10, 0.1).name('X');
                moonPosFolder.add(this.lights.moon.position, 'y', 0, 10, 0.1).name('Y');
                moonPosFolder.add(this.lights.moon.position, 'z', -10, 10, 0.1).name('Z');
                moonPosFolder.open();
                
                // 幽灵控制组
                const ghostFolder = this.gui.addFolder('幽灵设置');
                ghostFolder.add(this.objects.ghost1, 'intensity', 0, 5, 0.1).name('幽灵1强度');
                ghostFolder.add(this.objects.ghost2, 'intensity', 0, 5, 0.1).name('幽灵2强度');
                ghostFolder.add(this.objects.ghost3, 'intensity', 0, 5, 0.1).name('幽灵3强度');
                
                // 雾效控制组
                const fogFolder = this.gui.addFolder('雾效设置');
                fogFolder.addColor(this.fog, 'color').name('雾颜色');
                fogFolder.add(this.fog, 'near', 0.1, 5, 0.1).name('雾近距');
                fogFolder.add(this.fog, 'far', 5, 30, 0.1).name('雾远距');
                
                lightFolder.open();
            }
            
            /**
             * 处理窗口大小变化
             */
            handleResize() {
                // 更新相机宽高比
                this.camera.aspect = this.container.clientWidth / this.container.clientHeight;
                this.camera.updateProjectionMatrix();
                
                // 更新渲染器尺寸
                this.renderer.setSize(
                    this.container.clientWidth,
                    this.container.clientHeight
                );
                this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
            }
            
            /**
             * 更新幽灵动画
             * @param {number} elapsedTime - 经过的时间
             */
            updateGhosts(elapsedTime) {
                // 幽灵1动画路径 - 圆形轨迹
                this.objects.ghost1.position.x = Math.cos(elapsedTime * 0.8) * 5;
                this.objects.ghost1.position.z = Math.sin(elapsedTime * 0.8) * 5;
                this.objects.ghost1.position.y = Math.sin(elapsedTime * 3) + 0.5;
                
                // 幽灵2动画路径 - 椭圆形轨迹
                const offset2 = Math.sin(elapsedTime * 0.3) * 2;
                this.objects.ghost2.position.x = Math.cos(-elapsedTime * 0.5) * (8 + offset2);
                this.objects.ghost2.position.z = Math.sin(-elapsedTime * 0.5) * (8 + offset2);
                this.objects.ghost2.position.y = Math.sin(elapsedTime * 2) + 1;
                
                // 幽灵3动画路径 - 复杂轨迹
                this.objects.ghost3.position.x = Math.cos(-elapsedTime * 0.6) * 6;
                this.objects.ghost3.position.z = Math.sin(-elapsedTime * 0.6) * 6;
                this.objects.ghost3.position.y = Math.sin(elapsedTime * 2.5) + 
                                                Math.cos(elapsedTime * 1.5) + 1;
            }
            
            /**
             * 动画循环
             */
            animate() {
                // 获取经过的时间
                const elapsedTime = this.clock.getElapsedTime();
                
                // 更新幽灵位置
                this.updateGhosts(elapsedTime);
                
                // 更新控制器
                this.controls.update();
                
                // 渲染场景
                this.renderer.render(this.scene, this.camera);
                
                // 请求下一帧动画
                requestAnimationFrame(() => this.animate());
            }
            
            /**
             * 启动动画
             */
            startAnimation() {
                this.animate();
            }
        }
        
        // 页面加载完成后初始化场景
        window.addEventListener('load', () => {
            new HauntedHouseScene('sceneContainer');
        });
    </script>
</body>
</html>
    